class DataUtils::Geo::Locator
    attr_reader :processors
    def initialize()
        @processors = []
    end
    def add_processor(p)
        @processors << p
    end
    #search params
    #options --- :train => true, :confidence => [:zero,:city, :region, :country, :all]
    #	enable training mode and train when confidence is in the specified levels or pass all to train all
    def city_search(params, options = {})
    	clean_params(params)
    	raise "* :city param is required!" unless params[:city]
    	options[:train] ||= false
    	options[:confidence] ||= [:all]
        original_params = params.dup if options[:train]
        @processors.each do |p|
            params = p.process(params)
        end
        
        search_term = DataUtils::Geo::Locator.get_search_string(params)
        term_match = search_by_term(search_term)
        return term_match unless term_match.nil?
        country = params[:country]
        region = params[:region]
        city = params[:city]
        search_results = []
        
        countries = nil
        regions = nil
        cities = []
        
        confidence = :zero
        if(!country.nil?)
            countries = DataUtils::Geo::Country.find(:name => country)
        end
        if(!region.nil?)
        	if(countries)
        		countries.each do |c|
        			conditions = clean_params(:name => region, :country_id => c.id)
        			regions = DataUtils::Geo::Region.find(conditions)
        		end
        	else
        		countries = []
        		regions = DataUtils::Geo::Region.find(:name => region)
        		regions.each do |r|
        			countries << r.country
        		end
        	end
        end
        #city search
    	if(regions)
    		regions.each do |r|	
    			cities << DataUtils::Geo::City.find_first(:name => city, :region_id => r.id)
    		end
    		countries.each do |c|
    			cities << DataUtils::Geo::City.find_first(:name => city, :country_id => c.id)
    		end
    		cities.reject! {|c| c.nil?}
    		if(cities.empty?)
    			regions.each do |r|
    				search_results << DataUtils::Geo::SearchResult.new(:city => city, :region => r.name, :country => r.country.name, :confidence => :region)
    			end
    		end
    	elsif(countries)
    		countries.each do |c|
    			cities << DataUtils::Geo::City.find_first(:name => city, :country_id => c.id)
    		end
    		cities.reject! {|c| c.nil?}
    		if(cities.empty?)
    			countries.each do |c|
    				search_results << DataUtils::Geo::SearchResult.new(:city => city, :country => c.name, :confidence => :country)
    			end
    		end
    	end
    	cities.each do |c|
    		puts c
			search_results << DataUtils::Geo::SearchResult.new(:city => c.name, :region => c.region.name, :country => c.country.name, :confidence => :city)
		end
		search_results = search_results.sort_by {|result| result.confidence_val()}
		max_confidence = search_results[0].confidence unless search_results.empty?
        return search_results unless options[:train]
       	return search_results unless options[:confidence].include?(:all) || options[:confidence].include?(max_confidence)
        return train(original_params, search_results)
    end
    def self.add_search_term(params, location)
    	DataUtils::Geo::SearchMap.add_term(get_search_string(params), location)
   	end
    def self.add_location(params)
        raise "* malformed location!" unless params[:country]
        location = nil
        country = DataUtils::Geo::Country.find_or_create(:name => params[:country])
        location = country
        region = nil
        if(!params[:region].nil?)
            region = DataUtils::Geo::Region.find_or_create(:name => params[:region], :country_id => country.id)
            location = region
        end
        city = nil
        if(!params[:city].nil?)
            if(!region.nil?)
                city = DataUtils::Geo::City.find_or_create(:name => params[:city], :region_id => region.id)
            else
                city = DataUtils::Geo::City.find_or_create(:name => params[:city], :country_id => country.id)
            end
            location = city
        end
        return location
    end
    private
    def self.get_search_string(params)
        search_term = [params[:city], params[:region], params[:country]].join(',')
        return search_term
    end
    def train(params, search_results)
    	original_params = params.dup
        puts "search:"
        puts "-----------"
        puts "#{params[:city]}, #{params[:region]}, #{params[:country]}"
		puts ""
        puts "result(s):"
        puts "-----------"
        search_results.each_with_index do |result,i|
        	puts "#{i+1}: #{result}"
        end
        puts ""
        print "enter the number of the correct result.(enter if none): "
        response = gets.chomp()
        return search_results if(response.match(/^\d/))
        correct = false
        while(!correct)
		    print "enter city: "
		    params[:city] = gets.chomp().strip()
		    print "enter region: "
		    params[:region] = gets.chomp().strip()
		    print "enter country: "
		    params[:country] = gets.chomp().strip()
		    puts "-----------"
		    search_result = search(params)
		    puts search_result
		    puts ""
        	print "is this correct?(y/n): "
        	response = gets.chomp()
        	correct = response.match(/^y/i)
        end
        save_training(original_params, search_result)
        return search_result
    end
    def save_training(orig_params, search_result)
    	needs_search_term = false
    	orig_city = orig_params[:city]
    	orig_region = orig_params[:region]
    	orig_country = orig_params[:country]
    	city = search_result.city
    	region = search_result.region
    	country = search_result.country
    	needs_search_term = true if(is_alias?(orig_city, city, :city) == false)
    	needs_search_term = true if(is_alias?(orig_region, region, :region) == false)
    	needs_search_term = true if(is_alias?(orig_country, country, :country) == false)
    	location = DataUtils::Geo::Locator.add_location(search_result.to_hash())
    	DataUtils::Geo::Locator.add_search_term(orig_params, location) if needs_search_term
    end
    #nil - the same or old is nil -- doesnt matter
    #true - yes
    #false - no
    def is_alias?(old, new, type)
    	return nil if(old.nil? || old.downcase == new.downcase)
    	return true if(DataUtils::Processing::Aliases.alias_exists?(type, old))
		print "is #{old} an alias of #{new}?(y/n): "
		response = gets.chomp
		if(response.match(/^y/i))
			DataUtils::Processing::Aliases.add(type, old, new)
			return true
		end
		return false
    end 
    def search_by_term(search_term)
        result = DataUtils::Geo::SearchMap.search(search_term)
        return result
    end
    def clean_params(params)
    	params.reject! {|k,v| v.nil? || v.empty?}
    end
end
